#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#include <vector>
#include <functional>

void PrintLog()
{
    std::cout << "这个是一个日志历程" << std::endl;
}
void OperMysql()
{
    std::cout << "这个是一个操作数据库的历程" << std::endl;
}
void CheckNet()
{
    std::cout << "这个是一个检测网络状态的历程" << std::endl;
}

using func_t = std::function<void(void)>;
std::vector<func_t> funcs; // 保存大量的函数方法

//如果一个线程想让另一个线程派发任务
void LoadTask()
{
    funcs.push_back(PrintLog);
    funcs.push_back(OperMysql);
    funcs.push_back(CheckNet);
}

void HandlerALLTask() // 处理所以的任务
{
    for (const auto &func : funcs)
        func();
}

void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0)
    {
        std::cerr << "error string: " << strerror(errno) << "error code: " << errno << std::endl;
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK); // 把老的设置成非阻塞的
}

int main()
{
    // 测试阻塞
    //  char buffer[64];
    //  while (true)
    //  {
    //      printf(">>> ");
    //      fflush(stdout);
    //      ssize_t n = read(0, buffer, sizeof(buffer) - 1); // 卡在这里了，没有数据，0号文件描述符没有就绪，当我输入数据，回车一下，就立马向下进行，这个过程就是阻塞IO
    //      //read在这里做了两件事情，一是等，(但是前提是先检测条件是否就绪),二是拷贝
    //      if (n > 0)
    //      {
    //          buffer[n-1] = 0;//-1,去掉一个\n
    //          std::cout << "echo# " << buffer << std::endl;
    //      }
    //  }

    // 测试非阻塞
    char buffer[64];
    SetNonBlock(0); // 这里设置非阻塞，就会报错，因为read时底层数据没有就绪，是因为读取检测时调用太快，文件描述符还没准备好，来不及输入就报错了
    LoadTask();
    while (true)
    {
        printf(">>> ");
        fflush(stdout);
        // 一般会返回三种状态，1、成功 2、结束 3、出错（一但底层没有数据就绪，就以出错的形式返回，但是不算真正的出错，)
        ssize_t n = read(0 /*4*/, buffer, sizeof(buffer) - 1); // 这了写成4，就会输出错误，因为4并没有被打开
        if (n > 0)
        {
            buffer[n - 1] = 0; //-1,去掉一个\n
            std::cout << "echo# " << buffer << std::endl;
        }
        else if (n == 0) // 表示读到了文件结尾,linux中输入ctrl+D表示读到了文件结尾
        {
            std::cout << "end file" << std::endl;
            break;
        }
        else
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 底层数据没有准备好，希望你下次继续来检查
                HandlerALLTask();//底层没有数据的时候，就去做其他事情，输入数据了，就输出我输入的数据
                sleep(1);
                std::cout << "data not ready" << std::endl;
                continue;
            }
            else if (errno == EINTR)
            {
                // 这次IO被信号中断，也需要重新读取
                continue;
            }
            else
            {
                // 一旦fd被设置成为非阻塞，
                // 这就是真正的错误
                std::cerr << "read error?? "
                          << "error string: " << strerror(errno) << "error code: " << errno << std::endl;
            }
            // break;//把这里去掉，再sleep(1)，就会正常读你输入的信息，但是读完后还是会有报错，因为大部分都是在等待,所以前面加了个if
        }
        // sleep(1);
    }
    return 0;
}